package edu.chl.niclun.projectwebshop.nbp.DataBase;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import edu.chl.niclun.projectwebshop.nbp.Entities.Client;
import edu.chl.niclun.projectwebshop.nbp.Entities.Movie;
import edu.chl.niclun.projectwebshop.nbp.Entities.Actor;
import edu.chl.niclun.projectwebshop.nbp.Entities.Actor_;
import edu.chl.niclun.projectwebshop.nbp.Entities.Client_;
import edu.chl.niclun.projectwebshop.nbp.Entities.Movie_;
import utils.NonexistentEntityException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import javax.transaction.UserTransaction;

/**
 * Class to persist objects to the database
 */
public class WebshopjpaCtrl implements IJpaCtrl {

    // UserTransaction not used in non container (i.e. for now)
    private final UserTransaction utx;
    // Get from Database
    private final EntityManagerFactory emf;
    private final EntityManager em;
    private final EntityTransaction et;
    
    public WebshopjpaCtrl(UserTransaction utx, EntityManagerFactory emf) {
        this.utx = utx;
        this.emf = emf;
        this.em = emf.createEntityManager();
        this.et = em.getTransaction();
        
        
        ImportMovies movieDB = new ImportMovies();
        ArrayList<Movie> moviesFromTxt = movieDB.getMovies();
        for(Movie m1 : moviesFromTxt){
            create(m1);
        }
        
    }
     //used to persist movie objects to the database 
    @Override
    public void create(Object t) {
        try{
            Movie m = (Movie)t;
            m.setAddedToDb(new Date());
            et.begin();
            em.persist(m);
            et.commit();
            Logger.getAnonymousLogger().log(Level.INFO, "Movie Created!");
        }catch(NumberFormatException e) {
             Logger.getAnonymousLogger().log(Level.INFO, "Invalid movie info! No movie was created!");
        }
    }
    //remove from database
    @Override
    public void destroy(Long id) throws NonexistentEntityException {
        try {
            Movie movie = em.find(Movie.class, id);
            if(movie != null) {
                et.begin();
                em.remove(movie);
                et.commit();
                Logger.getAnonymousLogger().log(Level.INFO, "Movie with Id '" + id + "' was deleted!");
            }else {
                Logger.getAnonymousLogger().log(Level.INFO, "No Movie with Id '" + id + "' was found!");
            }
        }catch(NumberFormatException e) {
            Logger.getAnonymousLogger().log(Level.INFO, "Invalid movie info was entered!");
        }
    }
    //edit in database
    @Override
    public void edit(Object t) throws NonexistentEntityException {
        if(t != null) {
            try {
                Movie m = (Movie)t;
                Long id = m.getMovieId();
                Movie movie = em.find(Movie.class, id);
                if(movie != null) {
                    et.begin();
                    et.commit();
                    Logger.getAnonymousLogger().log(Level.INFO, "Movie with Id '" + movie.getMovieId() + "' was updated!");
                }else {
                   Logger.getAnonymousLogger().log(Level.INFO, "No Movie with Id '" + m.getMovieId() + "' was found!");
                }
            }catch(NumberFormatException e) {
                Logger.getAnonymousLogger().log(Level.INFO, "Invalid movie info was entered!");
            }
        }
    }
    //get movie by id
    @Override
    public Object findEntity(Long id) {
        return em.find(Movie.class, id);
    }
    
    //get all movies
    @Override
    public List findEntities() {
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Movie> query = builder.createQuery(Movie.class);
        Root<Movie> m = query.from(Movie.class);
        query.select(m);
        return em.createQuery(query).getResultList();
        
    }

    @Override
    public List findEntities(int maxResults, int firstResult) {
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Movie> query = builder.createQuery(Movie.class);
        Root<Movie> m = query.from(Movie.class);
        query.select(m);
        List<Movie> list = em.createQuery(query).getResultList();
        List<Movie> productList = new ArrayList<Movie>();
        for(int i = firstResult; i < firstResult+maxResults && i < list.size(); i++) {
            productList.add(list.get(i));
        }
        return productList;
    }
    //returns the last added movies in the database
    @Override
    public List<Movie> findLastAdded(int maxResults) {
        List<Movie> allMovies = findEntities();
        
        TreeSet<Movie> sortedMovies = new TreeSet<Movie>(new MovieComparator());
        for(Movie m : allMovies)
            sortedMovies.add(m);
        
        List<Movie> lastAdded = new ArrayList<Movie>();
        for(int i = 0; i < maxResults && sortedMovies.size() > 0; i++) {
            lastAdded.add(sortedMovies.pollFirst());
        }
        return lastAdded;
    }
    //search by genre
    @Override
    public List<Movie> searchGenre(String string){
        /*CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Movie> cq = builder.createQuery(Movie.class);
        Root<Movie> movie = cq.from(Movie.class);
        
        
        Path<List> genre = movie.get(Movie_.genres);
        cq.where(builder.equal(genre, string));
       
        TypedQuery<Movie> q = em.createQuery((cq));
        List<Movie> genreList = q.getResultList();*/
        
        List<Movie> allMovies = findEntities();
        List<Movie> genreList = new ArrayList<Movie>();
        
        for(Movie movie : allMovies){
            String[] genres = movie.getGenreAsString().split(", ");                
            for(int i = 0; i<genres.length; i++)
                if(genres[i].equals(string))
                    genreList.add(movie);
        }
        return genreList;
    }
    //search for an actor
    @Override
    public List<Movie> searchActor(String string){
        
        String[] name = string.split(" ");
        ArrayList<Movie> movieList = new ArrayList<Movie>();            
        
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Actor> cq = builder.createQuery(Actor.class);
        Root<Actor> actor = cq.from(Actor.class);

        Path<String> first = actor.get(Actor_.firstName);        
        Path<String> last = actor.get(Actor_.lastName);
        TypedQuery<Actor> q = em.createQuery(cq);        
        List<Actor> actorList = new ArrayList<Actor>(); 
        
        
        cq.where(builder.equal(first, name[0]));        
        q = em.createQuery(cq);       
        for(Actor act : q.getResultList()){
            if(!actorList.contains(act))
                actorList.add(act);
        }
        
        cq.where(builder.equal(last, name[0]));
        q = em.createQuery(cq);  
        for(Actor act : q.getResultList()){
            if(!actorList.contains(act))
                actorList.add(act);
        }
        
        if(name.length > 1){
             cq.where(builder.equal(first, name[1]));
             q = em.createQuery(cq);
             for(Actor act : q.getResultList()){
                 if(!actorList.contains(act))
                    actorList.add(act);
            }

             cq.where(builder.equal(last, name[1]));
             q = em.createQuery(cq);
             for(Actor act : q.getResultList()){
                if(!actorList.contains(act))
                    actorList.add(act);
             }
        }
         for(Actor act : actorList ){
             for(Movie movie : act.getMovies()){
                 if(!movieList.contains(movie))
                    movieList.add(movie);
             }
         }
     
        return movieList;
    }
    //search for title
    @Override
    public List<Movie> searchTitle(String string){
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Movie> cq = builder.createQuery(Movie.class);
        Root<Movie> movie = cq.from(Movie.class);

        Path<String> title = movie.get(Movie_.title);
        
        cq.where(builder.equal(title, string));
        
        TypedQuery<Movie> q = em.createQuery(cq);
        
        List<Movie> movieList = q.getResultList();
        
        return movieList;
    }

    @Override
    public EntityManager getEntityManager() {
        return em;
    }
     //get number of movies in the database
    @Override
    public int getEntityCount() {
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Movie> query = builder.createQuery(Movie.class);
        Root<Movie> m = query.from(Movie.class);
        query.select(m);
        return em.createQuery(query).getResultList().size();
    }
    //moviecomparator for compare movieobjects
    private static class MovieComparator implements Comparator<Movie> {
        @Override
        public int compare(Movie m1, Movie m2) {
            return m1.compareDate(m2);
        }
    }
    //add a client/user to the database
    @Override
    public void addClient(Client t){
        Client client = (Client)t;
            et.begin();
            em.persist(client);
            et.commit();
    }
    //get a client from database
    @Override
    public List<Client> getClient(String email){
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Client> cq = builder.createQuery(Client.class);
        Root<Client> client = cq.from(Client.class);

        Path<String> title = client.get(Client_.email);
        
        cq.where(builder.equal(title, email));
        
        TypedQuery<Client> q = em.createQuery(cq);
        
        List<Client> clientList = q.getResultList();
        
        return clientList;
    }
}